#include "MainWindow.h"
#include "ShellAccess.h"
#include "DocumentWindow.h"

#include <QMdiSubWindow>
#include <QDockWidget>
#include <QCoreApplication>

ShellAccess::ShellAccess(MainWindow * main_window)
    : _main_window(main_window)
    , _module_collector({QString(QCoreApplication::applicationDirPath() + "/modules").toStdString()})
{
    Q_ASSERT(main_window);
}

QFileSystemModel & ShellAccess::filesystem_model()
{
    return _main_window->filesystem_model();
}

void ShellAccess::sendGlobal(const QString & text, shell::MessageSeverity severity)
{
    _main_window->sendGlobal(text, severity);
}

bool ShellAccess::openFile(const QString &fileName)
{
    return _main_window->openFile(fileName);
}

const QAction * ShellAccess::edit_undo_action() const { return _main_window->edit_undo_action(); }
const QAction * ShellAccess::edit_redo_action() const { return _main_window->edit_redo_action(); }
#ifndef QT_NO_CLIPBOARD
const QAction * ShellAccess::cut_action() const { return _main_window->cut_action(); }
const QAction * ShellAccess::copy_action() const { return _main_window->copy_action(); }
#endif

void ShellAccess::displayCursorPosition(const QString & info) { _main_window->displayCursorPosition(info); }
void ShellAccess::displayStatistics(const QString & info) { _main_window->displayStatistics(info); }

const std::shared_ptr<shell::DocumentAdaptor_interface> ShellAccess::getDocumentAdaptor(const QString & path, bool create)
{
    DocumentCollector &                 docs        = _main_window->document_collector();
    std::shared_ptr<shell::DocumentAdaptor_interface>  
                                        doc_adaptor = docs.find(path);

    if (!doc_adaptor && create) {
        shell::Document_plugin * doc_plugin = _main_window->findDocumentPlugin(QFileInfo(path).suffix());
        if (doc_plugin) {
            doc_adaptor = doc_plugin->createDocumentAdaptor(*this);
            if (doc_adaptor) {
                QString error_text = doc_adaptor->loadFile(path);
                if (!error_text.isEmpty()) {
                    return {};
                }
                _main_window->document_collector().add(path, doc_adaptor);
            }
        }
    }

    return doc_adaptor;
}

std::optional<QString> ShellAccess::getDocumentAdaptorPath(const std::shared_ptr<shell::DocumentAdaptor_interface> document_adaptor)
{
    return _main_window->document_collector().adaptorPath(document_adaptor);
}

const shell::ViewAdaptor_interface * ShellAccess::getCurrentViewAdaptor()
{
    DocumentWindow * document_window = _main_window->activeDocumentWindow();
    if (document_window == nullptr)
        return nullptr;

    return document_window->view_adaptor();
}

void ShellAccess::setCurrentDocumentWindowModified(bool modified)
{
    DocumentWindow * document_window = _main_window->activeDocumentWindow();
    QWidget * widget = document_window;
    while (widget) {
        widget->setWindowModified(modified);
        widget = qobject_cast<QWidget *>(widget->parent());
    }
}

bool ShellAccess::createPanel(const QString & , const QString & , const QString & )
{
    /// @todo Метод не реализован

    return false;
}

bool ShellAccess::risePanel(const QString & panel_name, const QString &title)
{
    const QVector<PanelPluginData> & panels = _main_window->panels();

    auto it = std::find_if(panels.begin(), panels.end(), [panel_name](const PanelPluginData & ppd){
                    return ppd.factory && ppd.plugin_id == panel_name;
                });

    if (it == panels.end())
        return false;

    for(const PanelData & pd : it->panels) 
        if (title.isEmpty() || pd.adaptor->title() == title) {
            pd.dock->setVisible(true);
            pd.dock->raise();
        }

    return true;
}

bool ShellAccess::setPanelTitle(const QString & panel_name, const QString & title)
{
    if (title.isEmpty())
        return false;

    const QVector<PanelPluginData> & panels = _main_window->panels();

    auto it = std::find_if(panels.begin(), panels.end(), [panel_name](const PanelPluginData & ppd){
                    return ppd.factory && ppd.plugin_id == panel_name;
                });

    if (it == panels.end())
        return false;

    for(size_t i = it->panels.size()-1; i < it->panels.size(); --i) {
        const PanelData & pd = it->panels[i];
        if (title.isEmpty() || pd.adaptor->title() == title) {
            QString panel_title = _main_window->combinePanelTitle(it->factory, title);
            
            // if ((it->factory->supports() & shell::PS_Pan_Singleton) == 0) {
            //     pd.dock->setObjectName(panel_title);
            // }

            pd.dock->setWindowTitle(panel_title);
            return true;
        }
    }

    return false;
}

bool ShellAccess::sendDataToPanel(const QString &panel_name, const QString &title, const QJsonObject &data)
{
    const QVector<PanelPluginData> & panels = _main_window->panels();

    auto it = std::find_if(panels.begin(), panels.end(), [panel_name](const PanelPluginData & ppd){
                    return ppd.factory && ppd.plugin_id == panel_name;
                });

    if (it == panels.end())
        return false;

    for(const PanelData & pd : it->panels) 
        if (title.isEmpty() || pd.adaptor->title() == title) {
            if (pd.adaptor->acceptData(data)) {
                if (pd.adaptor->title() != title)
                    pd.dock->setWindowTitle(_main_window->combinePanelTitle(it->factory, pd.adaptor->title()));
                pd.dock->setVisible(true);
                pd.dock->raise();
                return true;
            }
        }

    return false;
}

bool ShellAccess::sendDataToCurrentDocument(const QJsonObject & value)
{
    DocumentWindow * document_window = _main_window->activeDocumentWindow();
    if (document_window)
        return document_window->_tab_params[0].view->acceptData(value);

    return false;
}

QString ShellAccess::provideDataStorageFolder(const shell::Plugin_interface * plugin)
{
    QString path = QCoreApplication::applicationDirPath() + "/../data/";

    if (plugin == nullptr)
        path += "null";
    else
        path += "plugins/" + plugin->id();

    QDir dir(path);

    if (!dir.exists())
        dir.mkpath(dir.absolutePath());

    return dir.absolutePath();
}

QString ShellAccess::provideDataStorageFolder(const QString & feature_name)
{
    QDir dir(QCoreApplication::applicationDirPath() + "/../data/features/" + feature_name);

    if (!dir.exists())
        dir.mkpath(dir.absolutePath());

    return dir.absolutePath();
}

QString ShellAccess::performAllMacroSubstitutions(const QString & text)
{
    QDir    dataFolder(QCoreApplication::applicationDirPath()+"/../data");
    QString res = text;
    
    res = res.replace(shell::dataFolderMacros_const, dataFolder.absolutePath());

    return res;   
}

shell::LspAccess_interface * ShellAccess::getLspAccess(const QString & path_to_file)
{
    if (path_to_file.isEmpty())
        return & _main_window->lsp();

    if (_main_window->lsp().checkPath(path_to_file))
        return & _main_window->lsp();

    return nullptr;
}

shell::RunnerManagement_interface * ShellAccess::getRunnerManagement()
{
    return & _main_window->run_management();
}

void ShellAccess::notifyContentChanged(const QString & path_to_file)
{
    const DocumentWindow * dw = _main_window->activeDocumentWindow();

    if (dw == nullptr)
        return;

    if (path_to_file == dw->currentFile())
        _main_window->run_management().notifyContentChanged(path_to_file);
}
